package com.bhugo;
/**
 * @author benjamin
 * Driver for the Amazon IP instance merge program
 */
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

public class Driver {
	public static HashMap<IPStore,ArrayList<Integer>> instances = new HashMap<IPStore,ArrayList<Integer>>();
	static int lineNumber = 0;
	/**
	 * Resets the line Number
	 */
	public static void reset(){
		lineNumber = 0;
		instances.clear();
	}
	/**
	 * Splits a line into tokens (blank space,commas,periods and colons)
	 * @param line
	 * @return list of tokens
	 */
	public static ArrayList<String> splitIPAndInstances(String line){
		//split the array into delimited text (whitespace, commas, periods and colons)
		String[] splittedLine = line.trim().split("\\s+|,+|\\.+|:+");
		ArrayList<String> result = new ArrayList<String>(splittedLine.length);
		//remove any empty strings:
		for (int i = 0; i < splittedLine.length; ++i)
			if (!splittedLine[i].equals(""))
				result.add(splittedLine[i]);
		return result;
	}
	/**
	 * Regex to check if a line is too malformed to process:
	 * Requirements:
	 * 1. There must be an IPv4 address. Each token must be separated by a period, but there may be 
	 *    whitespace between tokens.
	 * 2. There may be a colon(s) separating the IP and elements
	 * 3. There can be any arbitrary number of comma (or whitespace) delimited elements. Commas may be
	 * 	  repeated for instance "1, 2,,3" to accommodate typos
	 * 4. A line may be left blank
	 * @param line
	 * @return true if line meets all the requirements
	 */
	public static boolean checkCompatibleFormat(String line){
		return line.matches("(\\s*)|((\\s*\\d{1,3}\\s*\\.+){3}(\\s*\\d{1,3}\\s*\\.*)(\\:)*(\\s*\\d*\\s*,*)*)");
	}
	/**
	 * Adds / merges an instance of an IP
	 * Note: this method does not remove duplicates
	 * @param line
	 */
	public static void addInstances(String line){
		++lineNumber;
		if (!checkCompatibleFormat(line)){
			System.err.println("Malformed line "+lineNumber+": ignoring input from that line");
			return;
		}
		ArrayList<String> splittedText = splitIPAndInstances(line);
		if (splittedText.size() == 0) //empty line
			return;
		//otherwise construct IP from first 4 tokens
		IPStore ip = null;
		Iterator<String> listIterator = splittedText.iterator();
		try{
			String[] addr = {listIterator.next(),listIterator.next(),
					listIterator.next(),listIterator.next()};
			ip = new IPStore(addr);
		} catch (Exception e) { //too few elements as well as illegal argument exception for the IPStore object
			System.err.println("Malformed IP address on line "+lineNumber+": ignoring input from that line");
			return;
		}
		//find the IP address or insert a new one into the map:
		if (!instances.containsKey(ip))
			instances.put(ip, new ArrayList<Integer>());
		//lookup the pointer to the list on the map once (linear average time):
		ArrayList<Integer> mappedList = instances.get(ip);  
		//now convert and append the rest of the tokens:
		while (listIterator.hasNext()){
			int convertedItem = Integer.parseInt(listIterator.next());
			mappedList.add(convertedItem);
		}
	}
	/**
	 * Removes duplicates for each IP in the map
	 * This method will have a average running time of k * (n*log(n) + n) (for k keys)
	 */
	public static void removeDuplicates(){
		Set<IPStore> keys = instances.keySet();
		for (IPStore ip : keys){
			//sort elements in n*log(n) time
			ArrayList<Integer> elements = instances.get(ip);
			Collections.sort(elements);
			Iterator<Integer> listIterator = elements.iterator();
			Integer prevElement = null;
			//remove any duplicates by comparing two consecutive values each time
			while (listIterator.hasNext()){
				Integer currentElement = listIterator.next();
				if (prevElement != null)
					if (prevElement.equals(currentElement))
						listIterator.remove();
				prevElement = currentElement; 
			}
		}
	}
	/**
	 * Reads input from a file
	 * @param filename
	 */
	public static void mergeFileIntoList(String filename){
		lineNumber = 0;
		try {
			Scanner s = new Scanner(new FileInputStream(filename));
			while (s.hasNextLine())
				addInstances(s.nextLine());
			removeDuplicates();
			s.close();
		} catch (FileNotFoundException e) {
			System.err.print("Could not open file "+filename);
		}
		
	}
	
	public static void main (String[] args){
		assert(args.length == 2);
		mergeFileIntoList(args[0]);
		mergeFileIntoList(args[1]);
		Set<IPStore> keys = instances.keySet();
		ArrayList<IPStore> sortedKeys = new ArrayList<IPStore>(keys.size());
		sortedKeys.addAll(keys);
		Collections.sort(sortedKeys);
		for (IPStore ip : sortedKeys){
			ArrayList<Integer> elements = instances.get(ip);
			System.out.print(ip.toString()+": ");
			Iterator<Integer> listIterator = elements.iterator();
			if (!listIterator.hasNext())
				System.out.println();
			while (listIterator.hasNext()){
				int elm = listIterator.next();
				if (listIterator.hasNext())
					System.out.print(elm+",");
				else
					System.out.println(elm);
			}
		}
	}
}
